import numpy as np
import plotly.graph_objects as go
# — Updated Constants for Accuracy
R_u = 8.314462618 # J/(mol·K), universal gas constant
T_trip = 273.16 # K, triple-point temperature
P_trip = 611.657 # Pa, triple-point pressure
# Melting (solid → liquid) at 0 °C
# ΔH_fus = 333.55 kJ/kg × 0.01801528 kg/mol ≈ 6006 J/mol
H_melt = 6006 # J/mol
# Volume change on melting at 0 °C: –1.639 cm³/mol = –1.639e–6 m³/mol
V_melt = -1.639e-6 # m³/mol
# Vaporization (liquid → gas) at 100 °C
# ΔH_vap(100 °C) ≈ 40.657 kJ/mol
H_vap = 40657 # J/mol
T_boil = 373.15 # K, boiling-point temperature at 1 atm
P_boil = 101325 # Pa, standard 1 atm
# Sublimation (solid → gas) at triple point:
# ΔH_sub ≈ ΔH_fus + ΔH_vap(0 °C) ≈ 6006 + (2.5e6 J/kg × 0.01801528 kg/mol) ≈ 51060 J/mol
H_sub = 6006 + (2.5e6 * 0.01801528) # ≈51060 J/mol
# Critical point
T_crit = 647.096 # K
P_crit = 22.064e6 # Pa
# — Temperature arrays
T_melt_arr = np.linspace(T_trip - 5, T_trip, 500) # melting curve
T_vap1_arr = np.linspace(T_trip, T_boil, 500) # liquid→vap (0–1 atm)
T_vap2_arr = np.linspace(T_boil - 50, T_crit, 500) # liquid→vap (1 atm–critical)
T_sub_arr = np.linspace(T_trip - 50, T_trip, 500) # sublimation curve
# — Compute pressures
P_melt_arr = P_trip + (H_melt / V_melt) * np.log(T_melt_arr / T_trip)
P_vap1_arr = P_trip * np.exp((H_vap / R_u) * (1/T_trip - 1/T_vap1_arr))
P_vap2_arr = P_boil * np.exp((H_vap / R_u) * (1/T_boil - 1/T_vap2_arr))
P_sub_arr = P_trip * np.exp((H_sub / R_u) * (1/T_trip - 1/T_sub_arr))
# — Build figure
fig = go.Figure([
go.Scatter(x=T_sub_arr, y=P_sub_arr, mode='lines',
name='Sublimation', line=dict(color='green', width=4)),
go.Scatter(x=T_melt_arr, y=P_melt_arr, mode='lines',
name='Fusion', line=dict(color='black', width=4)),
go.Scatter(x=T_vap1_arr, y=P_vap1_arr, mode='lines',
name='Vapour (0–1 atm)',
line=dict(color='blue', width=4, dash='dash')),
go.Scatter(x=T_vap2_arr, y=P_vap2_arr, mode='lines',
name='Vapour (1 atm–crit)',
line=dict(color='blue', width=4)),
go.Scatter(x=[T_trip], y=[P_trip], mode='markers+text', name='Triple Point',
marker=dict(size=12, color='black'),
text=['Triple Point'], textposition='bottom right'),
go.Scatter(x=[T_crit], y=[P_crit], mode='markers+text', name='Critical Point',
marker=dict(size=12, symbol='diamond', color='black'),
text=['Critical Point'], textposition='top left'),
])
fig.update_layout(
title='Water Phase Diagram (Clausius–Clapeyron)',
xaxis=dict(title='Temperature (K)', range=[200, 700]),
yaxis=dict(title='Pressure (Pa)', type='log'),
width=800,
height=600,
legend=dict(y=0.5),
template='plotly_white'
)
fig.show()
import numpy as np
import plotly.graph_objects as go
import ipywidgets as widgets
from IPython.display import display
# Constants
Rd = 287.05
g = 9.81
cp = 1005.0
Lv = 2.5e6
Rv = 461.5
epsilon = Rd / Rv
def gamma_m(T, q_s):
return (g / cp) * (1 + (Lv * q_s) / (Rd * T)) / (1 + (Lv**2 * q_s) / (cp * Rv * T**2))
def saturation_mixing_ratio(T, p):
es = 6.112 * np.exp((17.67 * (T - 273.15)) / (T - 29.65)) * 100
return epsilon * es / (p - es)
def compute_profiles(T0, p0=100000, z_top=15000, dz=100):
z = [0]
T_dry = [T0]
T_moist = [T0]
p = [p0]
while z[-1] < z_top and T_moist[-1] > 200:
T_dry.append(T_dry[-1] - dz * g / cp)
q_s = saturation_mixing_ratio(T_moist[-1], p[-1])
gamma = gamma_m(T_moist[-1], q_s)
T_moist.append(T_moist[-1] - dz * gamma)
p.append(p[-1] * np.exp(-dz * g / (Rd * T_moist[-1])))
z.append(z[-1] + dz)
return np.array(z), np.array(T_dry), np.array(T_moist)
def plot_profiles(T0):
z, T_dry, T_moist = compute_profiles(T0)
fig = go.Figure()
fig.add_trace(go.Scatter(x=T_dry, y=z, mode='lines', name='Dry Adiabat'))
fig.add_trace(go.Scatter(x=T_moist, y=z, mode='lines', name='Moist Adiabat'))
fig.update_layout(
title='Dry vs Moist Adiabatic Profiles',
xaxis_title='Temperature (K)',
yaxis_title='Height (m)',
yaxis=dict(autorange='reversed'),
height=600
)
fig.show()
widgets.interact(plot_profiles, T0=widgets.FloatSlider(value=300, min=280, max=320, step=1, description='T₀ [K]'));
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Constants
Rd = 287.05
g = 9.81
cp = 1005.0
Lv = 2.5e6
Rv = 461.5
epsilon = Rd / Rv
layers = [
(0, -0.0065, 288.15, 101325), # Troposphere (0–11 km)
(11000, 0.0, 216.65, 22632.1), # Tropopause (11–20 km)
(20000, 0.001, 216.65, 5474.89), # Lower Stratosphere (20–32 km)
(32000, 0.0028, 228.65, 868.02), # Middle Stratosphere (32–47 km)
(47000, 0.0, 270.65, 110.91), # Stratopause (47–51 km)
(51000, -0.0028, 270.65, 66.94), # Mesosphere (51–71 km)
(71000, -0.002, 214.65, 3.96), # Upper Mesosphere (71–84.852 km)
(84852, 0.0, 186.87, 0.3734) # Above 84.852 km (constant T assumed)
]
# Generate altitude array
h = np.linspace(0, 100000, 100)
T = np.zeros_like(h)
P = np.zeros_like(h)
# Initialize
T[0] = 288.15
P[0] = 101325
# Iterate through layers
for i in range(1, len(h)):
h_i = h[i]
for base_h, lapse, base_T, base_P in reversed(layers):
if h_i >= base_h:
delta_h = h_i - base_h
if lapse == 0:
T[i] = base_T
P[i] = base_P * np.exp(-g * delta_h / (R * base_T))
else:
T[i] = base_T + lapse * delta_h
P[i] = base_P * (T[i] / base_T) ** (-g / (R * lapse))
break
# Compute density from ideal gas law
rho = P / (R * T)
h_km = h / 1000
# Create 3 subplots
fig = make_subplots(
rows=1, cols=3,
shared_yaxes=True,
horizontal_spacing=0.1,
titles=("Temperature [K]", "Pressure [Pa]", "Density [kg/m³]")
)
# Add traces
fig.add_trace(go.Scatter(x=T, y=h_km, mode='lines', name='Temperature', line=dict(color='firebrick')), row=1, col=1)
fig.add_trace(go.Scatter(x=P, y=h_km, mode='lines', name='Pressure', line=dict(color='royalblue')), row=1, col=2)
fig.add_trace(go.Scatter(x=rho, y=h_km, mode='lines', name='Density', line=dict(color='green')), row=1, col=3)
# Update layout and reverse y-axis
fig.update_layout(
title="US Standard Atmosphere up to 100 km",
height=700,
width=1100,
showlegend=False,
)
fig.show()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[3], line 46
44 else:
45 T[i] = base_T + lapse * delta_h
---> 46 P[i] = base_P * (T[i] / base_T) ** (-g / (R * lapse))
47 break
49 # Compute density from ideal gas law
NameError: name 'R' is not defined
# Read Mauna Loa CO2 monthly means (auto-detect header after comments)
mauna_df = pd.read_csv(mauna_path, comment="#", header=0)
global_df = pd.read_csv(global_path, comment="#", header=0)
# Keep only numeric rows
mauna_df = mauna_df.dropna(subset=["year", "month", "average"])
global_df = global_df.dropna(subset=["year", "month", "average"])
# Create datetime columns
mauna_df["date"] = pd.to_datetime(dict(year=mauna_df.year.astype(int),
month=mauna_df.month.astype(int),
day=1))
global_df["date"] = pd.to_datetime(dict(year=global_df.year.astype(int),
month=global_df.month.astype(int),
day=1))
# Plot
fig = go.Figure()
fig.add_trace(go.Scatter(
x=mauna_df["date"], y=mauna_df["average"],
mode="lines", name="Mauna Loa CO₂ (ppm)",
line=dict(color="firebrick", width=2)
))
fig.add_trace(go.Scatter(
x=global_df["date"], y=global_df["average"],
mode="lines", name="Global Mean CO₂ (ppm)",
line=dict(color="royalblue", width=2, dash="dash")
))
fig.update_layout(
title="Mauna Loa vs Global Mean CO₂ Concentrations",
xaxis_title="Year",
yaxis_title="CO₂ concentration (ppm)",
hovermode="x unified",
width=900, height=500,
template="plotly_white",
legend=dict(x=0.01, y=0.99, bordercolor="black", borderwidth=1)
)
# Save interactive HTML
output_html = "/mnt/data/co2_mauna_global.html"
fig.write_html(output_html)
output_html
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[23], line 2
1 # Read Mauna Loa CO2 monthly means (auto-detect header after comments)
----> 2 mauna_df = pd.read_csv(mauna_path, comment="#", header=0)
3 global_df = pd.read_csv(global_path, comment="#", header=0)
5 # Keep only numeric rows
NameError: name 'mauna_path' is not defined